home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 051-075 / disk_069 / frags / frags.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  11KB  |  314 lines

  1. /*
  2.  * frags - a replacement for the frags command that will work on more than
  3.  *    512K of memory. In fact, I went ahead and set things up for 16Meg.
  4.  *
  5.  * Note - I'm trying Knuths "literate programming," and attempting to explain
  6.  * what's going on to some unknown audience. He claims this produces fewer
  7.  * bugs. If nothing else, it produces more comments.
  8.  *
  9.  * I'd like to know what others think of the result. You can send (electronic)
  10.  * mail to me as mwm@berkeley.edu, or ucbvax!mwm.
  11.  *
  12.  * Copyright (c) 1987, Mike Meyer
  13.  * All Rights Reserved
  14.  *
  15.  * This code may be freely redistributed, so long as the source is made
  16.  * available, and this notice is left in. You can even sell copies if you
  17.  * want to, if you make the source available complete with this notice.
  18.  */
  19.  
  20. #include <exec/types.h>
  21. #include <exec/exec.h>
  22. #include <exec/execbase.h>
  23.  
  24. /*
  25.  * Generic comment on printing - all we're doing is pushing numbers out
  26.  * on standard output, so we don't need the power (and hence size) of
  27.  * printf. We pass the size & count to Print_Count, which will do all
  28.  * the work. The original printfs have been left in place for reference.
  29.  * Print_Count doesn't return anything, so we declare it void.
  30.  */
  31. void Print_Count(long, long) ;
  32. /*
  33.  * Since we need to convert two longs to ASCII, we'll turn that into a
  34.  * subroutine that does all the work. It returns the same thing as
  35.  * Print_Count, and so has the same type.
  36.  */
  37. void Long_To_ASCII(long, char *, int) ;
  38.  
  39. /*
  40.  * We don't use the arguments, so use _main instead of main. Also, we're
  41.  * not going to return anything, so make _main() a void function.
  42.  */
  43. void
  44. _main() {
  45.     /*
  46.      * There is a list of headers, each of which describes memory of some
  47.      * single type. Each header contains a pointer to the list of chunks
  48.      * of memory that it tracks. hdr will be used to point to each element
  49.      * in list of headers in turn.
  50.      */
  51.     register struct MemHeader *hdr ;
  52.     /*
  53.      * Each header has a list of chunks of memory associated with it.
  54.      * chunk will be used to point to each chunk of every header.
  55.      */
  56.     register struct MemChunk *chunk ;
  57.     /*
  58.      * Each chunk contains the size of the chunk, and a pointer to the
  59.      * next chunk. This size includes the size & next pointer for that
  60.      * chunk, but we're going to ignore that. We copy the size to a
  61.      * register variable for speed.
  62.      */
  63.     register long    size ;
  64.     /*
  65.      * SysBase is a pointer to lots of interesting data. For this
  66.      * program, we're going to get the pointer to the first memory
  67.      * header structure.
  68.      */
  69.     extern struct ExecBase *SysBase ;
  70.     /*
  71.      * The 680[01]0 has a 24 bit address space. For each bit, we're
  72.      * going to count the number of chunks whose size has that bit
  73.      * as it's high-order bit. Hence, 24 slots in the array. However,
  74.      * we're going to have an extra slot for zero-sized chunks, which
  75.      * shouldn't occur, so the program won't die horribly if they
  76.      * occur. Thus, we have 25 slots. Chunks of size 2^(N-1) to (2^N)-1
  77.      * will be counted in slot N. Also, since there can be more than 16
  78.      * bits of 8-byte chunks, we need longs to hold the counter.
  79.      * Finally, declare it static so that it will be zero'ed by the
  80.      * loader.
  81.      */
  82.     static long Chunk_Count[25] ;
  83.     /*
  84.      * Given an array, we really need something to index it with. Being
  85.      * an old FORTRAN hacker, I tend to favor i.
  86.      */
  87.     register short i ;
  88.  
  89.  
  90.     /*
  91.      * We need to prevent other tasks from changing the memory list
  92.      * while we're walking it. Forbid() turns off task switching, so
  93.      * that we are the only task running.
  94.      */
  95.     Forbid() ;
  96.     /*
  97.      * There is a standard list structure in AmigaDOS. SysBase contains
  98.      * a pointer to the list header for the list of memory headers
  99.      * (MemList), and lh_Head is the first member of that list.
  100.      */
  101.     hdr = (struct MemHeader *) SysBase -> MemList . lh_Head ;
  102.     /*
  103.      * Each element in an AmigaDOS list starts with a standard Node
  104.      * structure, in this case called mh_Node. ln_Succ is the link to
  105.      * the successor node in this list, and is part of the standard node
  106.      * structure. Standard lists are a circular doubly-linked, with a
  107.      * distinguished header/trailer node. This node is distinguished by
  108.      * having a zero (false) successor. So we stop walking the list when
  109.      * mh_Node . ln_Succ of hdr is false.
  110.      */
  111.     while (hdr -> mh_Node . ln_Succ) {
  112.         /*
  113.          * Chunks are a singly linked list, holding the length of the
  114.          * chunk and a pointer to the next chunk. The end of the
  115.          * chunk is denoted by a false pointer. Since the trailer is
  116.          * not distinguished, we check for chunk itself to be zero,
  117.          * not for it's successor to be false.
  118.          *
  119.          * I suspect that a standard AmigaDOS list wasn't used as that
  120.          * would have made the smallest possible memory chunk larger.
  121.          */
  122.         for (chunk = hdr -> mh_First; chunk; chunk = chunk -> mc_Next) {
  123.             /*
  124.              * The size of the chunk is called mc_Bytes. Save it
  125.              * in a register for speed.
  126.              */
  127.             size = chunk -> mc_Bytes ;
  128.             /*
  129.              * Now, count how many times you can shift size right
  130.              * before it becomes zero. i right shifts before size
  131.              * goes to zero will mean that size was between 2^(i-1)
  132.              * and (2^i)-1 at the start of the loop (unless i is
  133.              * zero, which means that size was 0), so we want to
  134.              * increment the Chunk_Count slot i. The special case
  135.              * for a zero-size chunk just falls out.
  136.              */
  137.             for (i = 0; size; i += 1) size >>= 1 ;
  138.             Chunk_Count[i] += 1 ;
  139.             }
  140.         /*
  141.          * Since the successor is non-zero, we need to switch to it,
  142.          * and then go over the next headers list of chunks.
  143.          */
  144.         hdr = (struct MemHeader *) hdr -> mh_Node . ln_Succ ;
  145.         }
  146.     /*
  147.      * We're through playing with the memory list, so we use Permit()
  148.      * to turn task switching back on. This is called "being polite."
  149.      */
  150.     Permit() ;
  151.     /*
  152.      * The zero-length chunks don't fit the standard pattern, so
  153.      * print their count first. Use the same existence test as the
  154.      * standard loop, though.
  155.      */
  156.     if (Chunk_Count[0])
  157.         Print_Count((long) 0, Chunk_Count[0]) ;
  158. /*
  159.         printf("%8ld: %8d\n", (long) 0, Chunk_Count[0]) ;
  160. */
  161.     /*
  162.      * Finally, we want to print the results of all this. So, we
  163.      * let the index (i) iterate over the slots in the chunk counter,
  164.      * from 1 to 23. Slot 0 has already been handled.
  165.      */
  166.     for (i = 1; i <= 23; i += 1)
  167.         /* 
  168.          * If Chunk_Count for this slot is non-zero (true), we
  169.          * print it. Otherwise, we skip it.
  170.          */
  171.         if (Chunk_Count[i])
  172.             /*
  173.              * Chunk_Count[i] contains a count of the number of
  174.              * chunks of size 2^(i-1) to 2^(i-1). So print 2^(i-1)
  175.              * and the count for chunks in that size range. Once
  176.              * again, we need more than 16 bits for possible
  177.              * sizes, so print it as a long, and cast the size
  178.              * argument to long.
  179.              */
  180.             Print_Count(((long) 1) << (i - 1), Chunk_Count[i]) ;
  181. /*
  182.             printf("%8ld: %8d\n",
  183.                 ((long) 1) << (i - 1), Chunk_Count[i]) ;
  184. */
  185.     }
  186.  
  187. /*
  188.  * Print_Count - print the pair of longs in fields of 8 spaces, seperated
  189.  *    by a `:' and terminated by a newline.
  190.  */
  191. void
  192. Print_Count(first, second)
  193.     register long first, second;
  194.     {
  195.  
  196.     /*
  197.      * Each long goes into a field 8 wide. We'll need that number 8
  198.      * in a number of places, so give it a nice name.
  199.      */
  200. #define    OUTPUT_FIELD_SIZE    8
  201.     /*
  202.      * The full field to be printed has two fields, plus a colon and
  203.      * a newline. We need that width in a few places also, so we give
  204.      * it a name too.
  205.      */
  206. #define OUTPUT_SIZE    ((2 * OUTPUT_FIELD_SIZE) + 1 + 1)
  207.     /*
  208.      * Finally, we need a place to put the output string. So we declare
  209.      * a character array of the appropriate size.
  210.      */
  211.     char    buffer[OUTPUT_SIZE] ;
  212.  
  213.     /*
  214.      * Let's do the easy part first, and put in the colon and the
  215.      * newline. The array is zero-based, so OUTPUT_FIELD_SIZE is
  216.      * the correct index for the colon (the first character after
  217.      * the first output field), and OUTPUT_SIZE is one greater than
  218.      * the index of the last character, so we use OUTPUT_SIZE - 1.
  219.      */
  220.     buffer[OUTPUT_FIELD_SIZE] = ':' ;
  221.     buffer[OUTPUT_SIZE - 1] = '\n' ;
  222.     /*
  223.      * Now, we call Long_To_ASCII, which expects a pointer to the
  224.      * end of the field it's to fill, and the length of the field
  225.      * it's to fill. It'll put the number in place, and fill the
  226.      * rest of the field with spaces. See the previous comment for
  227.      * a discussion of the indices used.
  228.      */
  229.     Long_To_ASCII(first, &buffer[OUTPUT_FIELD_SIZE - 1],
  230.         OUTPUT_FIELD_SIZE) ;
  231.     Long_To_ASCII(second, &buffer[OUTPUT_SIZE - 2],
  232.         OUTPUT_FIELD_SIZE) ;
  233.     /*
  234.      * Finally, print the result. Just call Write() with the output
  235.      * buffer and it's length, telling it to use the Output() stream.
  236.      */
  237.     Write(Output(), buffer, OUTPUT_SIZE) ;
  238.     }
  239.  
  240. /*
  241.  * Long_To_ASCII - convert the first argument into an ASCII string ending
  242.  * at the second argument, and fill out the field to the third argument
  243.  * with spaces.
  244.  */
  245. void
  246. Long_To_ASCII(in, out, length)
  247.     register long in;
  248.     register char *out;
  249.     register int length;
  250.     {
  251.  
  252.     /*
  253.      * If in is zero, we just put in a single '0'.
  254.      */
  255.     if (in == 0) {
  256.         *(out--) = '0' ;
  257.         /*
  258.          * We've put in a digit, so reduce the length by 1.
  259.          */
  260.         length -= 1 ;
  261.         }
  262.     /*
  263.      * Otherwise, we need to strip the digits off of in and put them
  264.      * in the array.
  265.      */
  266.     else
  267.         while (in != 0) {
  268.             /*
  269.              * To prevent overflows, we check to make sure
  270.              * that there's space for this digit. If not,
  271.              * we just return, with no comment.
  272.              */
  273.             if (length == 0) return ;
  274.             /*
  275.              * The current low-order digit is merely in rem 10,
  276.              * which, if we add it to '0', will be the ASCII
  277.              * representation of that low-order digit.
  278.              */
  279.             *(out--) = (in % 10) + '0' ;
  280.             /*
  281.              * Once again, we've put in a digit, so we reduce
  282.              * length.
  283.              */
  284.             length -= 1  ;
  285.             /*
  286.              * Since we've got in's low order digit, we want
  287.              * the next lower order digit to be in place for
  288.              * the next loop iteration. This can be done by
  289.              * dividing in by 10.
  290.              */
  291.             in /= 10 ;
  292.             }
  293.     /*
  294.      * Now, for however much length is left, we put a space in out,
  295.      * and nudge out to the next space.
  296.      */
  297.     while (length-- != 0)
  298.         *(out--) = ' ' ;
  299.     }
  300.  
  301. /*
  302.  * To cut down on memory useage, we provide a stub for a routine pulled
  303.  * in by the default startup code in _main. This code cleans up
  304.  * dynamically allocated memory, which we don't need. So we flush the code
  305.  * here. By making this a smallcode, smalldata program, turning off stack 
  306.  * checking (nothing recursive, and only three routines, so who needs it?),
  307.  * and adding this, I've reduced the size of frags to 1644 bytes. Since the
  308.  * original frags is 1964 bytes long, I'm happy. Just out of curiosity, I'd
  309.  * like to know how large a binary Manx 3.4 produces.
  310.  */
  311. void
  312. MemCleanup() {}
  313.  
  314.